// SPDX-License-Identifier: GPL-2.0
/*
 * rtc_csp.c - CSP API of LomboTech RTC module
 *
 * Copyright (C) 2016-2018, LomboTech Co.Ltd.
 * Author: lomboswer <lomboswer@lombotech.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include <linux/time.h>
#include "rtc_csp.h"

#define KEY_FIELD	0xEE18

/*
 * csp_rtc_get_ver - get the version of RTC
 * @param: none
 *
 * return value of the RTC version
 */
u32 csp_rtc_get_ver(void *addr)
{
	reg_rtc_rtc_ver_t reg;

	reg.val = READREG32(addr);
	return reg.val;
}

/*
 * csp_rtc_rst - reset RTC
 * @param: none
 *
 * reset defaut config
 */
void csp_rtc_rst(void *addr)
{
	reg_rtc_rtc_rst_t reg;

	reg.val			= READREG32(addr);
	reg.bits.rst		= 1;

	WRITEREG32(addr, reg.val);
}

/*
 * csp_rtc_sel_clk_src0 - select source0 of RTC clock
 * @clk_src: 0, rcosc; 1, lfeosc
 *
 */
void csp_rtc_set_clk_src0(void *addr, u32 clk_src)
{
	reg_rtc_rtc_clk_ctrl_t reg;

	reg.val = READREG32(addr);
	reg.bits.clk_src0_sel = clk_src;
	reg.bits.key_field = KEY_FIELD;
	WRITEREG32(addr, reg.val);
}

/*
 * csp_rtc_clk_src0_enable - enable of clk source0(manual)
 * @clk_src: 1, rcosc; 0, lfeosc
 *
 */
void csp_rtc_clk_src0_enable(void *addr, u32 stat)
{
	reg_rtc_rtc_clk_ctrl_t reg;

	reg.val = READREG32(addr);
	reg.bits.clk_src0_en = stat;
	reg.bits.key_field = KEY_FIELD;
	WRITEREG32(addr, reg.val);
}

/*
 * csp_rtc_set_clk_src - set the CLK source of RTC
 * @clk_src: 1, LFEOSC; 0, RCOSC
 *
 * LFEOSC is recommended because it is high precision
 */
void csp_rtc_set_clk_src(void *addr, u32 clk_src)
{
	reg_rtc_rtc_clk_ctrl_t reg;

	reg.val			= READREG32(addr);
	reg.bits.sel		= clk_src;
	reg.bits.key_field	= 0xEE18;
	/* reg.val |= 0xEE180000; */

	WRITEREG32(addr, reg.val);
}

/*
 * csp_rtc_get_clk_src_stat - get source of RTC clock
 * @param: none
 *
 * return: 1, LFEOSC; 0, RCOSC
 */
u32 csp_rtc_get_clk_src_stat(void *addr)
{
	reg_rtc_rtc_clk_ctrl_t reg;

	reg.val = READREG32(addr);
	return reg.bits.stat;
}

/*
 * csp_rtc_ld_enable - RTC LFEOSC detect enable
 * @ld_enable: 1, enable; 0, disable
 *
 */
void csp_rtc_ld_enable(void *addr, u32 ld_enable)
{
	reg_rtc_rtc_ld_en_t reg;

	reg.val		= READREG32(addr);
	reg.bits.en	= ld_enable;

	WRITEREG32(addr, reg.val);
}

/*
 * csp_rtc_ld_int_enable - RTC LFEOSC detect interrupt enable
 * @ld_int_enable: 1, interrupt enable; 0, interrupt disable
 *
 */
void csp_rtc_ld_int_enable(void *addr, u32 ld_int_enable)
{
	reg_rtc_rtc_ld_int_en_t reg;

	reg.val		= READREG32(addr);
	reg.bits.en	= ld_int_enable;

	WRITEREG32(addr, reg.val);
}

/*
 * csp_rtc_get_ld_int_enable - get state of RTC LFEOSC detect interrupt enable
 * @param: none
 *
 * return: 1, interrupt enable; 0, interrupt disable
 */
u32 csp_rtc_get_ld_int_enable(void *addr)
{
	reg_rtc_rtc_ld_int_en_t reg;

	reg.val	= READREG32(addr);
	return reg.bits.en;
}

/*
 * csp_rtc_ld_int_clr - clear the state of RTC LFEOSC detect interrupt pending
 * @param: none
 *
 * the bit of interrupt is writing 1 clears pending
 */
void csp_rtc_ld_int_clr(void *addr)
{
	reg_rtc_rtc_ld_int_clr_t reg;

	reg.val		= READREG32(addr);
	reg.bits.clr	= 1;

	WRITEREG32(addr, reg.val);
}

/*
 * csp_rtc_get_ld_int_pending - get state of LFEOSC detect interrupt pending
 * @param: none
 *
 * return: 1, interrupt is pending; 0, interrupt is not pending
 */
u32 csp_rtc_get_ld_int_pending(void *addr)
{
	reg_rtc_rtc_ld_int_pending_t reg;

	reg.val = READREG32(addr);
	return reg.bits.pending;
}

/*
 * csp_rtc_set_ymd - set date(year, month, day)
 * @day: range is depend on month
 * @month: 1~12
 * @year: 1900~2100
 *
 */
void csp_rtc_set_ymd(void *addr, u32 day, u32 month, u32 year)
{
	reg_rtc_rtc_ymd_t reg;

	assert((month >= MONTH_MIN) && (month <= MONTH_MAX));
	assert((year >= YEAR_MIN) && (year <= YEAR_MAX));
	assert((day >= DAYS_MIN) && (day <= BIG_MON_DAYS));

	if (month == 2) {
		if ((((year % 4) == 0) && ((year % 100) != 0)) ||
		      (year % 400 == 0))
			assert(day <= LPYEAR_MON_DAYS);
		else
			assert(day <= NLPYEAR_MON_DAYS);
	} else if ((month != 2) && (month != 4) && (month != 6) &&
		   (month != 9) && (month != 11))
		assert(day <= BIG_MON_DAYS);

	else
		assert(day <= LIT_MON_DAYS);

	reg.val = READREG32(addr);
	reg.bits.day	= day;
	reg.bits.month	= month;
	reg.bits.year	= year;

	WRITEREG32(addr, reg.val);
}

/*
 * csp_rtc_get_ymd - get date(year, month, day)
 * @param: none
 *
 * return: date
 */
u32 csp_rtc_get_ymd(void *addr)
{
	reg_rtc_rtc_ymd_t reg;

	reg.val = READREG32(addr);
	return reg.val;
}

/*
 * csp_rtc_set_week - set week
 * @week: range 0~6; 0, sunday; 1~6, monday~saturday
 *
 */
void csp_rtc_set_week(void *addr, u32 week)
{
	reg_rtc_rtc_week_t reg;

	assert((week >= WEEK_MIN) && (week <= WEEK_MAX));

	reg.val = READREG32(addr);
	reg.bits.week = week;

	WRITEREG32(addr, reg.val);
}

/*
 * csp_rtc_get_week - get week
 * @param: none
 *
 * return: week: 0, sunday; 1~6, monday~saturday
 */
u32 csp_rtc_get_week(void *addr)
{
	reg_rtc_rtc_week_t reg;

	reg.val = READREG32(addr);
	return reg.val;
}

/*
 * csp_rtc_set_hms - set time(second, minute, hour)
 * @second: 0~60
 * @minute: 0~60
 * @hour: 0~23
 *
 */
void csp_rtc_set_hms(void *addr, u32 second, u32 minute, u32 hour)
{
	reg_rtc_rtc_hms_t reg;

	assert((hour >= HOUR_MIN) && (hour <= HOUR_MAX));
	assert((minute >= MINUTE_MIN) && (minute <= MINUTE_MAX));
	assert((second >= SECOND_MIN) && (second <= SECOND_MAX));

	reg.val = READREG32(addr);
	reg.bits.second = second;
	reg.bits.minute = minute;
	reg.bits.hour = hour;

	WRITEREG32(addr, reg.val);
}

/*
 * csp_rtc_get_hms - get time(second, minute, hour)
 * @param: none
 *
 * retrun: time
 */
u32 csp_rtc_get_hms(void *addr)
{
	reg_rtc_rtc_hms_t reg;

	reg.val = READREG32(addr);
	return reg.val;
}

/*
 * csp_rtc_alarm_int_enable - RTC alarm interrupt enable
 * @alarm_int_enable: 1, interrupt enable; 0, interrupt disable
 *
 */
void csp_rtc_alarm_int_enable(void *addr, u32 alarm_int_enable)
{
	reg_rtc_rtc_alarm_int_en_t reg;

	reg.val = READREG32(addr);
	reg.bits.en = alarm_int_enable;

	WRITEREG32(addr, reg.val);
}

/*
 * csp_rtc_get_alarm_int_enable - get the state of RTC alarm interrupt enable
 * @param: none
 *
 * return: 1, interrupt enable; 0, interrupt disable
 */
u32 csp_rtc_get_alarm_int_enable(void *addr)
{
	reg_rtc_rtc_alarm_int_en_t reg;

	reg.val = READREG32(addr);
	return reg.bits.en;
}

/*
 * csp_rtc_alarm_int_clr - clear RTC alarm interrupt pending
 * @param: none
 *
 * the bit of interrupt is writing 1 clears pending
 */
void csp_rtc_alarm_int_clr(void *addr)
{
	reg_rtc_rtc_alarm_int_clr_t reg;

	reg.val = READREG32(addr);
	reg.bits.clr = 1;

	WRITEREG32(addr, reg.val);
}

/*
 * csp_rtc_get_alarm_int_pending - get the state of RTC alarm interrupt pending
 * @param: none
 *
 * return: 1, interrupt is pending; 0, interrupt is not pending
 */
u32 csp_rtc_get_alarm_int_pending(void *addr)
{
	reg_rtc_rtc_alarm_int_pending_t reg;

	reg.val = READREG32(addr);
	return reg.bits.pending;
}

/*
 * csp_rtc_alarm_match_enable - enable alarm match for counter
 * @alarm_match_enable_item:	0x0010, ALARM_DAY_MATCH_EN;
 *				0x0008, ALARM_WEEK_MATCH_EN;
 *				0x0004, ALARM_HOUR_MATCH_EN;
 *				0x0002, ALARM_MINUTE_MATCH_EN;
 *				0x0001, ALARM_SECOND_MATCH_EN;
 *
 */
void csp_rtc_alarm_match_enable(void *addr, int alarm_match_enable_item)
{
	reg_rtc_rtc_alarm_match_en_t reg;

	reg.val = 0;
	if (alarm_match_enable_item & ALARM_SECOND_MATCH_EN)
		reg.bits.sec_alarm_match_en = 1;
	if (alarm_match_enable_item & ALARM_MINUTE_MATCH_EN)
		reg.bits.min_alarm_match_en = 1;
	if (alarm_match_enable_item & ALARM_HOUR_MATCH_EN)
		reg.bits.hour_alarm_match_en = 1;
	if (alarm_match_enable_item & ALARM_WEEK_MATCH_EN)
		reg.bits.week_alarm_match_en = 1;
	if (alarm_match_enable_item & ALARM_DAY_MATCH_EN)
		reg.bits.day_alarm_match_en = 1;

	WRITEREG32(addr, reg.val);
}

/*
 * csp_rtc_alarm_match_get_enable - get alarm match enable stat
 * @param: none
 *
 * return alarm match enable stat
 */
u32 csp_rtc_alarm_match_get_enable(void *addr)
{
	reg_rtc_rtc_alarm_match_en_t reg;

	reg.val = READREG32(addr);
	return reg.val;
}

/*
 * csp_rtc_alarm_match_disable - disable alarm match
 * @param: none
 *
 * disable all match of alarm
 */
void csp_rtc_alarm_match_disable(void *addr)
{
	reg_rtc_rtc_alarm_match_en_t reg;

	reg.val = 0;
	WRITEREG32(addr, reg.val);
}

/*
 * csp_rtc_set_alarm_day - set the alarm day for match
 * @day: range depends on month
 *
 */
void csp_rtc_set_alarm_day(void *addr, u32 day)
{
	reg_rtc_rtc_alarm_day_match_t reg;

	assert((day >= DAYS_MIN) && (day <= BIG_MON_DAYS));

	reg.val = READREG32(addr);
	reg.bits.day_match = day;

	WRITEREG32(addr, reg.val);
}

/*
 * csp_rtc_get_alarm_day - get the alarm day
 * @param: none
 *
 * return: alarm day
 */
u32 csp_rtc_get_alarm_day(void *addr)
{
	reg_rtc_rtc_alarm_day_match_t reg;

	reg.val = READREG32(addr);
	return reg.bits.day_match;
}

/*
 * csp_rtc_set_alarm_week - set the alarm day for match
 * @week: 0~6
 *
 */
void csp_rtc_set_alarm_week(void *addr, u32 week)
{
	reg_rtc_rtc_alarm_week_match_t reg;

	assert((week >= WEEK_MIN) && (week <= WEEK_MAX));

	reg.val = READREG32(addr);
	reg.bits.week_match = week;

	WRITEREG32(addr, reg.val);
}

/*
 * csp_rtc_get_alarm_week - get the alarm week
 * @param: none
 *
 * return: alarm week
 */
u32 csp_rtc_get_alarm_week(void *addr)
{
	reg_rtc_rtc_alarm_week_match_t reg;

	reg.val = READREG32(addr);
	return reg.bits.week_match;
}

/*
 * csp_rtc_set_alarm_hms - set the alarm time(hour, minute, second) for match
 * @hour: 0~23
 * @minute: 0~59
 * @second: 0~59
 *
 */
void csp_rtc_set_alarm_hms(void *addr, u32 hour, u32 minute, u32 second)
{
	reg_rtc_rtc_alarm_hms_match_t reg;

	assert((hour >= HOUR_MIN) && (hour <= HOUR_MAX));
	assert((minute >= MINUTE_MIN) && (minute <= MINUTE_MAX));
	assert((second >= SECOND_MIN) && (second <= SECOND_MAX));

	reg.val = READREG32(addr);
	reg.bits.second_match = second;
	reg.bits.minute_match = minute;
	reg.bits.hour_match = hour;

	WRITEREG32(addr, reg.val);
}

/*
 * csp_rtc_get_alarm_hms - get the alarm time(hour, minute, second)
 * @param: none
 *
 * return: alarm time(hour, minute, second)
 */
u32 csp_rtc_get_alarm_hms(void *addr)
{
	reg_rtc_rtc_alarm_hms_match_t reg;

	reg.val = READREG32(addr);
	return reg.val;
}

/*
 * csp_rtc_wakeup_enable - enable RTC wakeup for power
 * @wakeup_enable: 1, enable; 0, disable
 *
 */
void csp_rtc_wakeup_enable(void *addr, u32 wakeup_enable)
{
	reg_rtc_rtc_wakeup_en_t reg;

	reg.val = READREG32(addr);
	reg.bits.wakeup_en = wakeup_enable;

	WRITEREG32(addr, reg.val);
}

/*
 * csp_rtc_su_enable - enable RTC su
 * @lfeosc_out_enable: 1, enable; 0, disable
 *
 */
void csp_rtc_su_enable(void *addr, u32 su_enable)
{
	reg_rtc_rtc_su_en_t reg;

	reg.val = READREG32(addr);
	reg.bits.en = su_enable;

	WRITEREG32(addr, reg.val);
}

/*
 * csp_rtc_set_speed - set speed of clock
 * @speed: speed up second rate
 *
 */
void csp_rtc_set_speed(void *addr, u32 speed)
{
	reg_rtc_rtc_su_en_t reg;

	reg.val = READREG32(addr);
	reg.bits.rate = speed;

	WRITEREG32(addr, reg.val);
}

/*
 * csp_rtc_get_ld_det_flg - get state of LFEOSC detect flag
 * @param: none
 *
 * return: 1, lfeosc; 0, rcosc
 */
u32 csp_rtc_get_ld_det_flg(void *addr)
{
	reg_rtc_rtc_ld_int_pending_t reg;

	reg.val = READREG32(addr);
	return reg.bits.det_flg;
}

/*
 * csp_rtc_get_low_vol_stat - get RTC voltage
 * @param: none
 *
 * return: 1, low voltage; 0, normal
 */
u32 csp_rtc_get_low_vol_stat(void *addr)
{
	reg_rtc_rtc_clk_ctrl_t reg;

	reg.val = READREG32(addr);
	return reg.bits.lvdet;
}

void csp_rtc_core_config(void *addr)
{
	reg_rtc_rtc_core_t reg;

	reg.val = readl(addr);
	reg.bits.ldo_stb = 1;
	WRITEREG32(addr, reg.val);
}

